﻿using RetroNET;
using RetroNET.Network;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace AccessNode
{
	class AccessNodeMain
	{
		static void Main(string[] args)
		{
			AccessNode node = new AccessNode();
			node.Startup();
		}
	}

    class AccessNode
    {
		 RetroNETCore core = new RetroNETCore();

		 public void Startup()
		 {
		

			 // Let's hook into everything in RNCore we care about...
			 core.delHandleClientInput += new HandleClientInputDelegate(ParseClientCommand);
			 core.delOnClientConnected += new OnClientConnectedDelegate(OnClientConnected);

			core.Setup();
			
			core.Start();

			Console.WriteLine("Press any key to exit...");
			Console.ReadKey();
			}

		 public bool ParseClientCommand(RNClient client, string msg) // ret true: we handled it; false: we didn't
		 {
			 Console.WriteLine("[AccessNode] Received command \"" + msg + "\" from client...");

			 string[] cmdTokens = msg.Split(new string[] { " ", "\t", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);

			 // If we're connected, we need to just send the data over.
			 // Any "control" commands are prefaced by "+++"
			 if (client.sockOut != null && client.sockOut.Connected)
			 {
				 // Just send the command 
				 try
				 {

					 core.SendDataToClient(client, "[AccessNode] Sending \"" + msg + "\" to server...");
					 byte[] rawData = System.Text.Encoding.ASCII.GetBytes(msg.Remove(0, 5));
					 client.sockOut.BeginSend(rawData, 0, rawData.Length, 0, new AsyncCallback(RemoteDataReceived), client);

					 client.sendbuffer = new byte[1024000];
					 return true;
				 }
				 catch (Exception ex)
				 {
					 Console.WriteLine("[AccessNode] Socket error: " + ex);
					 Console.WriteLine("Not connected to a remote server?");
					 return true;
				 }

			 }

			 switch (cmdTokens[0].ToUpper())
			 {
				 case "CONNECT":
					 if (cmdTokens.Length < 2) // Did we just say "CONNECT" without a destination?
					 {
						 core.SendDataToClient(client, "Connect to where?");
						 return true;
					 }

					 // We're assuming that cmdTokens[1] is the address to connect to and cmdTokens[2] is the port (if it's specified).
					 // This is only for now, as the TGDs will handle connection info.
					 // If there are any extra tokens, ignore them.

					 int destPort = ((cmdTokens.Length > 2) ? Int32.Parse(cmdTokens[2]) : 23);

					 core.SendDataToClient(client, "Attempting to connect to " + cmdTokens[1] + " on port " + destPort + "...");

					 try
					 {
						 IPHostEntry ipHostInfo = Dns.GetHostEntry(cmdTokens[1]);
						 IPAddress ipAddress = ipHostInfo.AddressList[0];
						 IPEndPoint remoteEP = new IPEndPoint(ipAddress, destPort);
						 client.sockOut = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
						 client.sockOut.SendTimeout = 30;
						 client.sockOut.BeginConnect(remoteEP, new AsyncCallback(RemoteConnected), client);
						 return true;
					 }
					 catch(Exception ex)
					 {
						Console.WriteLine("[AccessNode] Outbound socket error: " + ex);
						 return false;
					 }
	
						 

				 default:
					 Console.WriteLine("[AccessNode] No handler here, falling through to base server!");
					 return false;
			 }
		 }

		 public void HandleRemoteDataDelegate(RNClient client, byte[] data)
		 {
			 Console.WriteLine("[AccessNode] Received " + data.Length + " bytes of data from " + client.sockOut.RemoteEndPoint.ToString());
			 core.SendDataToClient(client, "[AccessNode] Received " + data.Length + " bytes of data from " + client.sockOut.RemoteEndPoint.ToString());


		 }


		 void RemoteConnected(IAsyncResult ar)
		 {
			 try
			 {
				RNClient client = (RNClient) ar.AsyncState;

            // Complete the connection.
            client.sockOut.EndConnect(ar);

            //Console.WriteLine("Socket connected to {0}!", client.sockOut.RemoteEndPoint.ToString());
				core.SendDataToClient(client, "[AccessNode] Connected!");

				client.sockOut.BeginReceive(client.recvbuffer, 0, client.recvbuffer.Length, 0, new AsyncCallback(RemoteDataReceived), client);

				 /*
				 // Empty out client.sendbuffer because we get strangeinputcharacters on the first send...
				client.sendbuffer = new byte[1024000];*/

			} catch (Exception ex) {
            Console.WriteLine("[AccessNode] Outbound completion socket error: " + ex);
        }

		 }

		 void RemoteDataReceived(IAsyncResult ar)
		 {
			 try
			 {
				 RNClient client = (RNClient)ar.AsyncState;

				 // Complete the connection.
				 int numBytes = client.sockOut.EndReceive(ar);

				 //Console.WriteLine("Data received from {0}...", client.sockOut.RemoteEndPoint.ToString());

				 //Console.WriteLine(System.Text.Encoding.ASCII.GetString(client.recvbuffer.Take(numBytes).ToArray()));

				 core.SendDataToClient(client, client.recvbuffer.Take(numBytes).ToArray(), false);
				 client.recvbuffer = new byte[1024000];
				 client.sockOut.BeginReceive(client.recvbuffer, 0, client.recvbuffer.Length, 0, new AsyncCallback(RemoteDataReceived), client);
			 }
			 catch (Exception ex)
			 {
				 Console.WriteLine("[AccessNode] Outbound receive error: " + ex);
			 }

		 }

		 public void OnClientConnected(RNClient client)
		 {
			 // Send the welcome banner to client
			 core.SendDataToClient(client, "Welcome to RetroNET!"); // Added extra newline here for formatting
			 core.SendDataToClient(client, "Server date/time: " + DateTime.Now.ToString());

			 ParseClientCommand(client, "connect www.clusterfsck.us 1023");
		 }

	 }
}
